package com.sfx.distributeLockStock.lock;

import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: sfx
 * Date: 2024-01-04
 * Time: 14:34
 */
@RequiredArgsConstructor
public class RedisLock extends AbstractLock {


    public RedisLock(StringRedisTemplate stringRedisTemplate, String lockName) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.lockName = lockName;
        this.uuid = UUID.randomUUID().toString();
    }

    private static final String LUA_SCRIPT_LOCK = "if (redis.call('exists', KEYS[1]) == 0) then redis.call('set', KEYS[1], ARGV[1]); redis.call('pexpire', KEYS[1], ARGV[2]); return 1; else return 0; end";

    private static final String LUA_SCRIPT_UN_LOCK = "if (redis.call('exists', KEYS[1]) == 1) then if (redis.call('get', KEYS[1]) == ARGV[1]) then redis.call('del', KEYS[1]); return 1; else return 0; end else return 0; end";

    /**
     * 使用lua脚本
     *
     * @param expireTime 过期时间
     */
    @Override
    public void lock(Long expireTime) {
        while (true) {
            Long execute = stringRedisTemplate.execute(new DefaultRedisScript<>(LUA_SCRIPT_LOCK, Long.class),
                    Collections.singletonList(lockName), uuid, expireTime.toString());
            if (execute != null && execute.equals(1L)) {
                // 加锁成功之后就返回
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 加锁带有过期时间
     *
     * @param expireTime 过期时间
     */
    public void lock(Long expireTime, TimeUnit unit) {
        // 加锁
        while (true) {
            // 使用 set nx key val
            Boolean absent = this.stringRedisTemplate.opsForValue().setIfAbsent(lockName, uuid, expireTime, unit);
            if (absent != null && absent) {
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }



    @Override
    public void lock() {
        // 加锁
        while (true) {
            // 使用 set nx key val
            Boolean absent = this.stringRedisTemplate.opsForValue().setIfAbsent(lockName, uuid);
            if (absent != null && absent) {
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 使用分布式锁解决锁误删问题 - 使用lua脚本
     */
    @Override
    public void unlock() {
        stringRedisTemplate.execute(new DefaultRedisScript<>(LUA_SCRIPT_UN_LOCK, Long.class),
                Collections.singletonList(lockName), uuid);
    }

//    /**
//     * 使用分布式锁解决锁误删问题 - 使用lua脚本
//     */
//    @Override
//    public void unlock() {
//        stringRedisTemplate.delete(lockName);
//    }


//    /**
//     * 使用分布式锁解决锁误删问题
//     */
//    @Override
//    public void unlock() {
//        String res = stringRedisTemplate.opsForValue().get(lockName);
//        if (uuid.equals(res)) {
//            this.stringRedisTemplate.delete(lockName);
//        }
//    }
}